/*
 * lrvm.C
 *
 *  Created on: Nov 26, 2011
 *
 */


#include "rvm.h"

#define verbose 0

char *generate_filepath(const char *directory,  const char *segname){
	int length = strlen(directory) + strlen(segname) + 2;
	char *filepath;
	filepath = (char *)malloc(sizeof(char)*length);
	sprintf(filepath, "%s/%s", directory, segname);
	filepath[length-1] = '\0';
	return filepath;
}

rvm_segment* create_segment(const char *segname, int size_to_create, void *seg_block , long int fd )
{
	//cout << " enter creation of segment" <<endl;
	rvm_segment *segment ;
	char *segname_temp;
	segment = (rvm_segment*)malloc(sizeof(rvm_segment));
	segment->memory = seg_block ;
	segment->segname = segname ;
	segment->size = size_to_create ;
	segment->seg_fd = fd ;
	segment->state = UNMODIFIED ;
	segment->logmemory = NULL ;
	//cout << " leaving creation of segment" <<endl;

	segname_temp = (char*)malloc( sizeof(char) * ( strlen(segname) + 5 )) ;
	strcpy(segname_temp,segname);
	strcat(segname_temp,".log");

	segment->logname = segname_temp;
	if(verbose) cout<< "[create segment :"<<__LINE__<<"]" << segment->logname <<endl ;
	return segment;


}

rvm_segment* find_via_name(vector < rvm_segment*> seglist, char *segname)
{
	vector <rvm_segment*>::iterator it_seg;
	rvm_segment *segment;

	for ( it_seg = seglist.begin() ; it_seg < seglist.end() ; it_seg++)
	{
		segment = *it_seg;
		if ( strcmp(segment->segname,segname) == 0)
		{
			if(verbose) cout<< "[find_via_name:"<<__LINE__<<"]" << "Found"<<endl ;
		 return segment ;
		}
	}
	return NULL ;
}

int findindex_via_name(vector < rvm_segment*> seglist, const char *segname)
{
	vector <rvm_segment*>::iterator it_seg;
	rvm_segment *segment;

	int i = 0;
	for ( it_seg = seglist.begin() ; it_seg < seglist.end() ; it_seg++)
	{
		segment = *it_seg;
		if ( strcmp(segment->segname,segname) == 0)
			{
			if(verbose) cout<< "[findindex_via_name:"<<__LINE__<<"]" << "Found"<<endl ;
			return i ;

			}
		i++;
	}
	return -1 ;
}


rvm_segment* find_via_memory(vector < rvm_segment*> seglist, void *segbase)
{
	vector<rvm_segment*>::iterator it;
	rvm_segment *segment;
	if(verbose) cout<< "[find_via_memory:"<<__LINE__<<"]" << "entered find memory"<<endl ;
	for (it = seglist.begin() ; it != seglist.end() ; it++)
	{
		segment = *it;
		if(verbose) cout<< "[find_via_memory:"<<__LINE__<< "]" <<segment->segname<<endl ;

		if ( segment->memory == segbase ){
			//if(verbose) cout<< "[find_via_memory:"<<__LINE__<<"]" << "Found"<<endl ;
			if(verbose) cout<< "...found segment via memory"<<endl ;
			return segment;
		}
	}
	return NULL;

}

rvm_t rvm_init(const char *directory)
{

	  rvm_t rvm_x;

	  char *filename ;
	  struct stat st;

	  rvm_x =  (rvm*)malloc ( sizeof(rvm) );
	  memset(rvm_x, 0, sizeof(rvm));

	  if(verbose) printf("[rvm_init:%u] Initializing rvm library\n", __LINE__);


	  if(stat(directory, &st)) {

      if(verbose) printf("[rvm_init:%u] Creating Directory %s\n", __LINE__, directory);

	    mkdir(directory, S_IRWXU|S_IRWXG|S_IRWXO);
	  }






	  rvm_x->rootdir = directory ;
	  //filename = generate_filepath (directory, "lrvm.log");


	  // RECOVERY.......
	  	  DIR *dir;
	  	  struct dirent *ent;
	  	  dir = opendir(directory);
	  	  if (dir != NULL)
	  		  {
	  			  while ((ent = readdir (dir)) != NULL)
	  			  {
	  				  string str = ent->d_name ;
	  				  size_t found=str.find(".log");
	  				  if(found!=string::npos)
	  					  {

	  					  const char *logfilename = str.c_str();
	  					  string str_sub = str.substr(0,str.size()-4);
	  					  const char *filename = str_sub.c_str();
	  					  if (verbose) cout << "[rvm_init:"<<__LINE__<<"]"<< "Log :"<<logfilename << endl;
	  					  if (verbose) cout << "[rvm_init:"<<__LINE__<<"]"<< "Segment :" <<filename << endl;
	  					   int result = remove(generate_filepath(rvm_x->rootdir,filename));
	  					   if(verbose) if ( result == 0 ) puts ( "File successfully Deletion" );    else { cout << "Deleting error\n" ;}

	  					   int result2 = rename(generate_filepath(rvm_x->rootdir,logfilename) ,generate_filepath(rvm_x->rootdir,filename) );
	  					   if(verbose) if ( result2 == 0 ) puts ( "File successfully renamed" );    else { cout << "renaming error\n"; }

	  					   /*
	  					    **** using system() command
	  					   string cmd_str = "mv ";
	  					   cmd_str.append(generate_filepath(rvm_x->rootdir,logfilename));
	  					   cmd_str.append(" ");
	  					   cmd_str.append(generate_filepath(rvm_x->rootdir,logfilename));
	  					   char *command = cmd_str.c_str();
	  					   cout << "system command : " << command << endl ;
	  					   //system(command);

	  					    */



	  					  }





	  		      }
	  		    closedir (dir);
	  		  }


	  return rvm_x;


}

void *rvm_map(rvm_t rvm, const char *segname, int size_to_create)
{

	//map<string,rvm_segment*>::iterator it_seg;
	long int fd,fd_log;
	char *filename ;
	char *logfilename;
	int filesize , logfilesize ;
	void *data , *seg_block , *logdata;
	int check;
	rvm_segment *new_segment;

	if (!rvm ) return NULL ;  // check if rvm is not NULL

	if (find_via_name(rvm->seglist, (char*) segname) )
	{
		 cout << "[rvm_map:"<<__LINE__<<"]"<<  " Segment cannot be mapped again - " << endl ;
		 exit(0) ;
	}

    // doing the creation of .log
	string log_str = segname;
	log_str.append(".log");
	const char* log = log_str.c_str();

	logfilename = generate_filepath(rvm->rootdir, log) ;

	fd_log = open(logfilename, O_RDWR | O_CREAT, 0666);
	logfilesize =  lseek(fd_log , 0L , SEEK_END);
	if(verbose) cout << "[rvm_map:"<<__LINE__<<"]" << " Initial logfilesize : " << logfilesize << endl;


	// doing the creation of segment
	filename = generate_filepath(rvm->rootdir, segname);  // open or create filename with segment name
	fd = open(filename, O_RDWR | O_CREAT, 0666);
	check = lockf(fd,F_LOCK,0L);
	if (check == -1 ) { cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< " Lock not successful \n";}
	filesize =  lseek(fd , 0L , SEEK_END);
	if(verbose) cout << "[rvm_map:"<<__LINE__<<"]" << " Initial filesize : " << filesize << endl;





	if  (filesize < size_to_create)   // checking to see if segment has to be extended or not
	{
		data = calloc(size_to_create - filesize, 1);
		write(fd, data, size_to_create - filesize);   // file being extended
		filesize = lseek(fd, 0L, SEEK_END);
		if(verbose) cout << "[rvm_map:"<<__LINE__<<"]"<< " Final filesize " <<filesize << endl ;



		if (filesize != size_to_create)
		{
			cout<< " Error extending existing segment file" << endl ;
			cout <<" Initial size" << filesize << endl ;
			cout << " Size to create " << size_to_create << endl ;
			exit(0);
			return NULL ;
		}



	}
	else if (size_to_create < filesize )
	{
		cout << "Request size is smaller than current segment size" << endl ;
		exit(0);
		return NULL;
	}



	if  (logfilesize < size_to_create)   // checking to see if log  has to be extended or not
	{



		 // doing the extensio of .log
		logdata = calloc(size_to_create - logfilesize, 1);
		write(fd_log, logdata, size_to_create - logfilesize);
		logfilesize = lseek(fd_log, 0L, SEEK_END);
		if(verbose) cout << "[rvm_map:"<<__LINE__<<"]"<< " Final logfilesize " <<logfilesize << endl ;

		if (logfilesize != size_to_create)
		{
			cout<< " Error extending existing log file" << endl ;
			cout <<" Initial size" << logfilesize << endl ;
			cout << " Size to create " << size_to_create << endl ;
			exit(0);
			return NULL ;
		}




	}
	else if (size_to_create < logfilesize )
	{
		cout << "Request size is smaller than current segment size" << endl ;
		exit(0);
		return NULL;
	}








	int y = lseek(fd, 0, SEEK_SET);  						// reading from file block ---> memory

	seg_block = calloc(size_to_create,1);
	y = read(fd, seg_block, size_to_create);
	check = lockf(fd,F_ULOCK,0L);
	if (check == -1 ) { cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< "Unlock Lock not successful \n";}
	close(fd);
	close(fd_log);

	new_segment = create_segment(segname, size_to_create, seg_block , fd );

	rvm->seglist.push_back(new_segment);

	return  seg_block;

}

void rvm_unmap(rvm_t rvm, void *segbase)
{

	map<string,rvm_segment*>::iterator it_seg;
	rvm_segment *segment ;

	if (!rvm) {
		printf("[rvm_unmap: %u ]Error in rvm structure\n", __LINE__);
		return;
	}

	/* remove from the transaction list */



	/* remove from the segment list */
	segment = find_via_memory(rvm->seglist, segbase);
	if (segment != NULL)
	{
		if(verbose) cout << "[rvm_unmap:"<<__LINE__<<"]"<< "Found Mapped Segment: " << segment->segname <<endl;
		segment->state = UNMAPPED ;
		free(segment->memory);
		free(segment->logmemory);
		int index = findindex_via_name(rvm->seglist, segment->segname);
		if(verbose) cout << "[rvm_unmap:"<<__LINE__<<"]"<< "Removing Mapped Segment : " << segment->segname <<" @ index : "<< index << endl;
		free(segment);
		rvm->seglist.erase (rvm->seglist.begin()+index);


	}
	else
	{
		cout << "[rvm_unmap:"<<__LINE__<<"]"<< "Segment Not present for unmap"  <<endl;
		exit(0);
	}


}

void rvm_destroy(rvm_t rvm, const char *segname)
{
	if (!rvm ) return ;
	rvm_segment *segment ;
	//if(verbose)

	segment = find_via_name(rvm->seglist,(char*)segname);
	if (segment != NULL )
	{
		if (segment->state == UNMAPPED )
		{
			remove(generate_filepath(rvm->rootdir,segname)); // deleting the segment file from directory
			int i = findindex_via_name(rvm->seglist,(char*)segname) ;
			vector <rvm_segment*> seglist =  rvm->seglist;
			free(segment);  // freeing the segment before erasing from list
			(rvm->seglist).erase((rvm->seglist).begin() + i);// erasing from the vector
			//seglist.erase( seglist.begin() +  i);
			//rvm->seglist = seglist ;

			if (find_via_name(rvm->seglist , (char*)segname ) != NULL) cout << "[rvm_destroy:"<<__LINE__<<"]" << "not deleted"<<endl;

			 if(verbose) cout << "[rvm_destroy:"<<__LINE__<<"]"<< " Segment destroyed :"<<segname << endl ;
		}
		else
		{
		 if(verbose) cout << "[rvm_destroy:"<<__LINE__<<"]"<< " Requested segment is currently mapped hence cannot be destroyed" << endl ;
		 return ;
		}
	}
	else
		{
		if(verbose) cout << "[rvm_destroy:"<<__LINE__<<"]"<< " Segment Not Found" << endl ;
		remove(generate_filepath(rvm->rootdir,segname)); // deleting the segment file from directory


		// removing the *.log file too
		string log_str = segname;
		log_str.append(".log");
		const char* log = log_str.c_str();
		remove(generate_filepath(rvm->rootdir,log));

		}

}

trans_t rvm_begin_trans(rvm_t rvm, int numsegs, void **segbases)
{

	if (!rvm ) return NULL ;

	rvm_segment *segment;

	/* Check if segments are being modified by other transactions or not*/
	for ( int i = 0 ; i < numsegs ; i++)
	{
		if(segment = find_via_memory( rvm->seglist,segbases[i]  )) //makes sure that segment is not null (intentionally kept "=" sign )
		{
			//cout << "[rvm_begin_trans:"<<__LINE__<<"]"<< "segment->state :" << segment->state << endl;
		  if (segment->state == TRANS_BEGUN )
			  {
			  cout << "[rvm_begin_trans:"<<__LINE__<<"]"<< "RETURNING -1 (trans_t): " << endl;
			  return (trans_t)-1;
			  }
		  else
		  	  {
			   /// do nothing coz later on you will be adding to a new transaction
		  	  }
		}
		else
		{
			cout << "[rvm_begin_trans:"<<__LINE__<<"]"<< " Given segment not yet mapped\n";
			exit(0) ; // arbitrary number for failure
		}

	}

	/*Initializing Transaction */
	trans_t new_transac;
	new_transac = (trans_t)malloc(sizeof(transaction));
	new_transac->rvm = rvm ;
	new_transac->state = NOTCOMMITTED;
	new_transac->numsegs = numsegs;

	for(int i =0 ; i < numsegs ; i++)
	{
	segment = find_via_memory( rvm->seglist,segbases[i] );
	segment->state = TRANS_BEGUN ;
	/*ensuring that the latest copy of commited memory is always backed up*/
	segment->logmemory = calloc(segment->size,1)  ;
	memcpy(segment->logmemory,segment->memory,segment->size);

    new_transac->sub_seg_list.push_back(segment);
    if (verbose ) cout << "[rvm_begin_trans:"<<__LINE__<<"]"<< new_transac->sub_seg_list.size() << endl;

	}

	// as of now region_list is empty



	/*Processing */

	if (verbose ) cout << "[rvm_begin_trans:"<<__LINE__<<"]"<< " Transaction Returned Successfully : SubseglistSize:" << new_transac->sub_seg_list.size() << endl;
	return new_transac ;

}

void rvm_about_to_modify(trans_t tid, void *segbase, int offset, int size)
{
	rvm_segment *segment ;
	region *region_ptr ;
	if (verbose) cout << "[rvm_about_to_modify:"<<__LINE__<<"]"<< "TID Subsegment list size :" << tid->sub_seg_list.size() << endl;

	if (tid == NULL) { cout << "[rvm_about_to_modify:"<<__LINE__<<"]"<< "Null transaction \n"; return; }


	segment = find_via_memory(tid->sub_seg_list,segbase);

	if ( segment == NULL )
	{
		cout << "[rvm_about_to_modify:"<<__LINE__<<"]"<< " Segment not present in Transaction\n";
		return ;
	}

	for ( int i = 0 ; i < tid->region_list.size() ; i++)
	{
		region_ptr = tid->region_list[i];
		if ( region_ptr->offset == offset && region_ptr->size == size && region_ptr->seg_ptr == segment )
		{
			// Do nothing
			return ;
		}

	}

	/*Initializing the region */

	region_ptr = ( region*)malloc( sizeof(region) ) ;
	region_ptr->offset = offset ;
	region_ptr->size = size ;
	region_ptr->seg_ptr = segment ;
	tid->region_list.push_back(region_ptr);

	//Updating the offset & offsetsize to modify





	if (verbose) cout << "[rvm_about_to_modify:"<<__LINE__<<"]"<< " Region added to Transaction : " << tid->region_list.size() << endl;



}


void rvm_commit_trans(trans_t tid)
{
	int subseglist_size = tid->sub_seg_list.size() ;
	int regionlist_size = tid->region_list.size();
	rvm_segment *segment ;
	region *region_ptr ;
	void *temp;
	char *filename ;
	int fd , check ;


	if ( tid == NULL)
		{
		 return ;
		}

	if (  regionlist_size == 0  )
		{
			cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< " No regions in segments have been modified \n";
			//return ;
		}
	if ( subseglist_size == 0  )
	{
		cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< " Transaction is either not valid or No segments Initialized \n";
		return ;
	}




	for (int  i = 0 ; i < subseglist_size ; i++)
	{
		segment = tid->sub_seg_list[i] ;
		segment->state = UNMODIFIED ;


		memcpy(segment->logmemory,segment->memory,segment->size);

		filename = generate_filepath(tid->rvm->rootdir,segment->logname);  // open or create filename with segment name

		fd = open(filename, O_RDWR | O_SYNC , 0666);


		for (int j = 0 ; j < regionlist_size ; j++ )
		{
			region_ptr = tid->region_list[j] ;
			if (region_ptr->seg_ptr == segment )
			{
				//copy the corresponding offset into
				lseek(fd,region_ptr->offset  , SEEK_SET);
				check = lockf(fd,F_LOCK,0L);
				if (check == -1 ) { cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< " Lock not successful \n";}

				temp = malloc(region_ptr->size);
				memcpy(temp,segment->memory+region_ptr->offset,region_ptr->size);
				if (verbose) cout << "writing this :" << temp<<endl;
				write(fd, temp, region_ptr->size);
				free(temp);

				check = lockf(fd,F_ULOCK,0L);
				if (check == -1 ) { cout << "[rvm_commit_trans:"<<__LINE__<<"]"<< " Unlock Lock not successful \n";}
			}
		}

		// remove all the regions-transaction once commited
		while(!tid->region_list.empty())
		{
			tid->region_list.pop_back();
		}

		close(fd);
	}
	tid->state =  COMMITTED ;
	if (verbose) cout << "[rvm_commit_trans::"<<__LINE__<<"]"<< " All segments committed to file \n";

}


void rvm_abort_trans(trans_t tid) //- undo all changes that have happened within the specified transaction.
{
	int subseglist_size = tid->sub_seg_list.size() ;
		rvm_segment *segment ;
		char *filename ;
		int fd , check ;
		if ( subseglist_size == 0 || tid == NULL )
		{
			cout << "[rvm_abort_trans:"<<__LINE__<<"]"<< " Transaction is either not valid or No segments Initialized \n";
			return ;
		}

	// copy from undologmemory back into segment memory
	for (int  i = 0 ; i < subseglist_size ; i++)
	{
		segment = tid->sub_seg_list[i] ;
		segment->state = UNMODIFIED ;

		memcpy(segment->memory,segment->logmemory,segment->size);

	}


	// remove all the regions-transaction once commited
	while(!tid->region_list.empty())
	{
		tid->region_list.pop_back();
	}

	if (verbose) cout << "[rvm_abort_trans::"<<__LINE__<<"]"<< " All segments abort in transaction \n";
	close(fd);
}

void rvm_truncate_log(rvm_t rvm)
{
	if (!rvm )
	{
		cout << "[rvm_truncate_log::"<<__LINE__<<"]"<< " rvm structure is void\n";
		exit(0);
	}

	for (int i = 0 ; i < rvm->seglist.size() ; i++)
	{

		remove(generate_filepath(rvm->rootdir,rvm->seglist[i]->segname));
		rename(generate_filepath(rvm->rootdir,rvm->seglist[i]->logname) , generate_filepath(rvm->rootdir,rvm->seglist[i]->segname));
	}

 // do nothing
}

